//
// Created by tang on 2022/1/11.
//

#ifndef DIARYTOOL_APP_CONFIG_HPP
#define DIARYTOOL_APP_CONFIG_HPP

#include <memory>
#include <string>
#include <vector>

#if _MSC_VER
#undef min
#undef max
#endif

#define FRAME_RATE_UNLIMIT  "超极致流畅(无帧率上限)"
#define FRAME_RATE_LIMIT24  "电影级流畅(约 24 FPS)"
#define FRAME_RATE_LIMIT30  "主机级流畅(约 30 FPS)"
#define FRAME_RATE_LIMIT60  "性能级流畅(约 60 FPS)"
#define FRAME_RATE_LIMIT144 "电竞级流畅(约144 FPS)"

namespace Config
{
    enum ConfigKey
    {
        KEY_START = 0,

        // RedMine相关设置
        KEY_REDMINE_SERVER_URL = KEY_START,
        KEY_REDMINE_API_KEY,

        // 数据库驱动设置
        KEY_SQL_DRIVER_NAME,

        // 垂直同步开关
        KEY_ENABLE_V_SYNC,
        KEY_FRAME_RATE_LIMIT,

        // 默认事项名
        KEY_DEFAULT_ITEM_NAME,
        KEY_SHOW_CLOSE_ISSUES,
        KEY_USE_DATE_EDITOR_ONLY, // 仅使用日期编辑器来选择日期，禁止手动输入日期
        KEY_DAILY_TOTAL_HOURS,    // 每日工作时长
        KEY_DISPLAY_USING_TAB,    // 是否使用tab来显示日记
        KEY_DISPLAY_ITEM_TIME,    // 在任务项前显示耗时信息

        // 任务功能
        KEY_LUNCH_BREAK_BEGIN_TIME, // 午休开始时间
        KEY_LUNCH_BREAK_END_TIME,   // 午休结束时间
        KEY_GET_OFF_WORK_TIME,      // 下班时间

        // 界面字体大小
        KEY_FONT_SIZE,

        // 界面样式
        KEY_APP_STYLE,
        // 窗口最大化显示
        KEY_WINDOW_MAXIMIZE,

        // 统计功能默认使用饼图
        KEY_DEFAUT_PIE_CHART,

        // 在线升级功能: 管理服务器
        KEY_UPGRADE_SERVER_URL,

        // 扩展功能
        KEY_SURVEYOR_SERVER_EN,
        KEY_SURVEYOR_SERVER_IP,

        KEY_COUNT
    };

    std::string& Value(ConfigKey key);
    inline std::string& Value(int key) { return Value((ConfigKey)key); }

    void Sync();
    void ViewClose(bool is_save);
    void RenderConfigurationsView(bool first_frame);

    struct ConfigData;

    class AppConfig
    {
        friend void Sync();
        friend void RenderConfigurationsView(bool first_frame);
        friend void ViewClose(bool is_save);
        ConfigData* d;
        friend std::string& Value(ConfigKey key);

        AppConfig();
        ~AppConfig();

        void LoadConfigs() const;
        void SaveConfigs() const;
    public:
        static AppConfig& Instance();

        [[nodiscard]] std::string Get(ConfigKey key, const std::string& defaultValue = "") const;
        [[nodiscard]] std::string Set(ConfigKey key, std::string newValue) const;
    };

    // New Renderable Config Impl
    struct IConfigItem
    {
        bool enabled{ true };
        virtual ~IConfigItem() = default;
        virtual void Render() = 0;
        virtual void Sync(bool read) = 0;
    };

    using ConfigItemPtr = std::shared_ptr<IConfigItem>;

    struct GroupItem : IConfigItem
    {
        std::string section_name;
        std::vector<ConfigItemPtr> children;
        void Render() override;
        void Sync(bool read) override;

        static ConfigItemPtr New(std::string section_name, const std::initializer_list<ConfigItemPtr>& children, bool enabled = true);
    };

    struct EditableItemBase : IConfigItem
    {
        // common data
        std::string  description;
        std::string  tooltip;
        std::string* associate_config;

        void Init(std::string desc, std::string* cfg, std::string tooltip, bool enabled);
    };

    struct IntItem : EditableItemBase
    {
        int value{ 0 };
        void Render() override;
        void Sync(bool read) override;

        static ConfigItemPtr New(std::string desc, std::string* cfg, std::string tooltip = {}, bool enabled = true);
    };

    struct RealItem : EditableItemBase
    {
        double value{ 0.0 };
        void Render() override;
        void Sync(bool read) override;

        static ConfigItemPtr New(std::string desc, std::string* cfg, std::string tooltip = {}, bool enabled = true);
    };

    struct SwitchItem : EditableItemBase
    {
        bool value{ false };
        std::string value_text[2]; // true string, false string
        void Render() override;
        void Sync(bool read) override;

        static ConfigItemPtr New(std::string desc, std::string* cfg, std::string on_text, std::string off_text = {}, std::string tooltip = {}, bool enabled = true);
    };

    struct EnumItem : EditableItemBase
    {
        int count;
        void* data;
        const char* (*item_getter)(void*, int);
        int selected;

        void Render() override;
        void Sync(bool read) override;

        static ConfigItemPtr New(std::string desc, std::string* cfg, int count, void* user_data, const char* (*item_getter)(void*, int), std::string tooltip = {}, bool enabled = true);
    };

    struct TextItem : EditableItemBase
    {
        std::string text;
        bool multi_line{ false };
        void Render() override;
        void Sync(bool read) override;

        static ConfigItemPtr New(std::string desc, std::string* cfg, bool multi_line = false, std::string tooltip = {}, bool enabled = true);
    };

    struct FunctionButton : IConfigItem
    {
        std::string description;
        std::string tooltip;
        bool same_line;
        void (*slot)();

        void Sync(bool read) override;
        void Render() override;

        static ConfigItemPtr New(std::string desc, bool same_line, void(*slot)(), std::string tooltip = {}, bool enabled = true);
    };

    struct TimeItem: EditableItemBase {
        int32_t values[3]{}; // hour,minute,second

        void Sync(bool read) override;
        void Render() override;

        static ConfigItemPtr New(std::string desc, std::string* cfg, std::string tooltip = {}, bool enabled = true);
    };
}

#endif //DIARYTOOL_APP_CONFIG_HPP
